#[derive(Debug)]
enum Direction {
    East = 0,
    West,
    North,
    South,
    Butt,
}

impl Direction {
    fn from_i32(v: i32) -> Direction {
        match v {
            x if x == Direction::East as i32 => Direction::East,
            x if x == Direction::West as i32 => Direction::West,
            x if x == Direction::North as i32 => Direction::North,
            x if x == Direction::South as i32 => Direction::South,
            _ =>  Direction::Butt,
        }
    }
}

enum Action {
    Say(String),
    MoveTo(i32, i32),
    ChangeColorRGB(u16, u16, u16),
}

fn fn_match(dire: &Direction) {
    let resut = match dire {
        Direction::East => {
            println!("match East");
            1
        }
        Direction::West | Direction::North => {
            println!("West or North");
            2
        }
        other => {
            println!("{:?}", other);
            3
        }
    };
    println!("resut = {:?}", resut);

    if let Direction::East = dire {
        println!("if let East");
    }
}

fn mode_bind() {
    let actions = [
        Action::Say("Hello".to_string()),
        Action::MoveTo(1, 2),
        Action::ChangeColorRGB(255, 0, 0),
    ];

    for action in actions {
        match action {
            Action::Say(s) => {
                println!("say {}", s);
            }
            Action::MoveTo(x, y) => {
                println!("move to ({}, {})", x, y);
            }
            Action::ChangeColorRGB(r, g, b) => {
                println!("change color to ({}, {}, {})", r, g, b);
            }
        }
    }
}

fn to_enum(v: i32) {
    let dire = Direction::from_i32(v);
    println!("dire = {:?}", dire);
}

fn multi_match() {
    let x = 1;
    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("something else"),
    }
}

fn seq_match() {
    let x = 5;
    match x {
        1..=5 => println!("one to five"),
        _ => println!("something else"),
    }

    let y = 'c';
    match y {
        'a'..='j' => println!("a to j"),
        'k'..='z' => println!("k to z"),
        _ => println!("something else"),
    }
}

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn bind_match() {
    let p @ Point { x: px, y: py } = Point { x: 10, y: 20 };
    println!("px = {}, py = {}", px, py);
    println!("p = {:?}", p);

    let point = Point { x: 10, y: 5 };
    if let p @ Point { x: 10, y} = point {
        println!("x is 10 and y is {}", y);
        println!("point = {:?}", p);
    } else {
        println!("x is NOT 10");
    }
}

fn test_match() {
    let dire = Direction::East;
    println!("\n------------- Test Match ----------------");
    fn_match(&dire);
    mode_bind();
    to_enum(1);
    to_enum(5);
    multi_match();
    seq_match();
    bind_match();
}

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn test_option() {
    let some_number = Some(5);
    let absent_number = None;
    let x = plus_one(some_number);
    let y = plus_one(absent_number);

    println!("\n------------- Test Option ---------------");
    println!("x = {:?}, y = {:?}", x, y);
}

fn main() {
    test_match();
    test_option();
}
